/**
 * Copyright (C) 2013 Christos Koufogiannakis
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 *     
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.easychart.chart;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint.Align;
import android.util.AttributeSet;

public class LineChart extends ChartBaseClass {		
	private float spaceBetweenPoints;
	private List<List<Double>> lineValues = new ArrayList<List<Double>>();
	private List<String> lineLabels = new ArrayList<String>();
	
	/** 
	 * Constructor for LineChart
	 * 
	 * @param context
	 * @param attrs
	 */
	public LineChart(Context context, AttributeSet attrs) {			
		super(context, attrs);	
	}
	
	/** 
	 * Constructor for LineChart
	 * 
	 * @param context
	 */
	public LineChart(Context context) {
		super(context);				
	}

	/**
	 * Set the list of values for the line and the list of corresponding labels
	 *  
	 * @param values is the list of values to be plotted in a line
	 * @param labels is the list of labels corresponding to the values. 
	 *  The size of lineLabels must be same as the size of barValues.
	 * 
	 * @throws IllegalArgumentException if lineValues is null or
	 *  the two lists have different sizes
	 */
	public void setValuesAndLabels(List<Double> values, List<String> labels)
			throws IllegalArgumentException {
		checkNullity(values);		
		addLine(values, null);
		
		if (labels == null || values.size() != labels.size()) {
			throw new IllegalArgumentException("The two list cannot be of different sizes");
		}
		else {
			super.setXLabels(labels);
		}
	}
	
	/**
	 * Set the list of values for the line and the list of corresponding labels
	 *  
	 * @param values is the list of values to be plotted in a line
	 * @param labels is the list of labels corresponding to the values. 
	 * @param color is the color to be used for the line. The size of
	 *  lineLabels must be same as the size of barValues.
	 * 
	 * @throws IllegalArgumentException if lineValues is null or
	 *  the two lists have different sizes
	 */
	public void setValuesAndLabels(List<Double> values,	List<String> labels,
			int color) throws IllegalArgumentException {
		checkNullity(values);
		addLine(values, null, color);
		
		if (labels == null || values.size() != labels.size()) {
			throw new IllegalArgumentException("The two list cannot be of different sizes");
		}
		else {
			super.setXLabels(labels);
		}	
	}
	
	/**
	 * Add one more line to the chart
	 * 
	 * @param values is the list of values to be plotted on the line
	 * @param label is the label of the line. If it is null, then
	 * empty string will be used as label
	 * 
	 * @throws IllegalArgumentException if lineValues is null
	 */
	protected void addLine(List<Double> values, String label)
			throws IllegalArgumentException {
		checkNullity(values);
		if (lineValues.size() >1 ){
			parameters.SHOW_LEGEND.setValue(true);
		}
		else {
			parameters.SHOW_LEGEND.setValue(false);
		}
		
		if (colorsArray.size() > lineValues.size()) {
			lineValues.add(values);
			if (label == null) {
				lineLabels.add("");
			}
			else {
				lineLabels.add(label);
			}
		}
		else {
			//not enough colors. create a new one
			Random rand = new Random();
			int newColor = rand.nextInt(0xffffff + 0xa) + 0xff000000;
			addLine(values, label, newColor);
		}
	}

	/**
	 * Add one more line to the chart
	 * 
	 * @param values is the list of values to be plotted on the line
	 * @param label is the label of the line. If it is null, then
	 * empty string will be used as label
	 * @param color is the color of the line
	 * 
	 * @throws IllegalArgumentException if lineValues is null
	 */
	void addLine(List<Double> values, String label, int color)
			throws IllegalArgumentException {
		checkNullity(values);	
		if (lineValues.size() >1 ){
			parameters.SHOW_LEGEND.setValue(true);
		}
		else {
			parameters.SHOW_LEGEND.setValue(false);
		}
		
		lineValues.add(values);
		if (label == null) {
			lineLabels.add("");
		}
		else { 
			lineLabels.add(label);
		}
		
		if (colorsArray.size() > lineValues.size()-1) {
			colorsArray.set(lineValues.size()-1, color);
		}
		else {
			colorsArray.add(color);
		}
	}
		
	@Override
	protected void onDraw(Canvas canvas) {	
		// find minY and maxY
		minY = Double.MAX_VALUE;
		maxY = Double.MIN_VALUE;	
		for (int i=0; i < lineValues.size(); i++){
			if (lineValues.get(i).isEmpty()) {
				continue;
			}
			minY = Math.min(minY, Collections.min(lineValues.get(i)));
			maxY = Math.max(maxY, Collections.max(lineValues.get(i)));
		}
			
		if ((xLabels == null || xLabels.isEmpty()) && lineValues.size() > 0){
			xLabels.clear(); 
			for (int i = 0; i< lineValues.get(0).size(); i++)
				xLabels.add("");
		}
		
		if (parameters.SHOW_LEGEND.getValue()){
			legendLabels = new String[lineLabels.size()];
			lineLabels.toArray(legendLabels);
		}
		
		// call the base class onDraw after the above calculations
		super.onDraw(canvas);
			
		// do this calculation before calling drawXLabels and drawLine
		spaceBetweenPoints = (plotWidth - 2 * parameters.PLOT_BORDER_TO_POINTS.getValue() 
				- 2 * parameters.VERTEX_POINT_WIDTH.getValue())/(xLabels.size()-1);
		super.drawXLabels(canvas, spaceBetweenPoints, parameters.PLOT_BORDER_TO_POINTS.getValue());		
		drawLines(canvas);
	}	
	
	/**
	 * Draw lines
	 * 
	 * @param canvas
	 */
	private void drawLines(Canvas canvas){		
		mPaint.setTextAlign(Align.CENTER);
		mPaint.setTextSize(vertexLabelTextSize);
		
		for (int line=0; line < lineValues.size(); line++){
			List<Double> values = lineValues.get(line);
			
			float prevX = -1;
			float prevY = -1;
			float x = plotLeftX + parameters.PLOT_BORDER_TO_POINTS.getValue();
			
			// the points and labels are printed after the line so that the line does not hide them
			for (int i=0; i < values.size(); i++){
				float pointHeight = (float) (maxPointHeight * (values.get(i) - minYLabel)/(yLabelRange));
				float y = plotBottomYInside - pointHeight;										
				
				if (parameters.SHOW_LINE.getValue() && (prevX >=0 && prevY >=0)) {
					// draw line from previous point to current point
					mPaint.setColor(colorsArray.get(line));
					mPaint.setStrokeWidth(parameters.LINE_WIDTH.getValue());
					canvas.drawLine(prevX, prevY, x, y, mPaint);			
				}
				
				if (parameters.SHOW_VERTEX_POINTS.getValue() && (prevX >=0 && prevY >=0)) {
					//draw previous point
					mPaint.setColor(colorsArray.get(line));					
					canvas.drawCircle(prevX, prevY, parameters.VERTEX_POINT_WIDTH.getValue(), mPaint);	
				}		
					
				if (parameters.SHOW_VERTEX_LABELS.getValue() && i>0) {
					mPaint.setColor(parameters.VERTEX_LABELS_TEXT_COLOR.getValue());
					String label =  labelValueToString(values.get(i-1), true);

					float textSize = adjustTextSize(label, vertexLabelTextSize, spaceBetweenPoints);
					mPaint.setTextSize(textSize);		
					float labelY;
		
					if (values.get(values.size()-1) >=0) {	
						labelY = prevY - parameters.VERTEX_TO_VERTEX_LABEL.getValue() 
								- parameters.VERTEX_POINT_WIDTH.getValue();	
						
						if (labelY - bounds.height() < plotTopY) {
							labelY = prevY + parameters.VERTEX_TO_VERTEX_LABEL.getValue()
									+ bounds.height() + parameters.VERTEX_POINT_WIDTH.getValue();	
						}
					}
					else{
						labelY = prevY + parameters.VERTEX_TO_VERTEX_LABEL.getValue()
								+  bounds.height() + parameters.VERTEX_POINT_WIDTH.getValue();	
		
						if (labelY > plotBottomY) {
							labelY = prevY - parameters.VERTEX_TO_VERTEX_LABEL.getValue() 
									- parameters.VERTEX_POINT_WIDTH.getValue();	
						}
					}
					canvas.drawText(label, prevX , labelY, mPaint);				
				}			

				prevX = x;
				prevY = y;
				x += spaceBetweenPoints;
			}
				
			if (parameters.SHOW_VERTEX_POINTS.getValue() && values.size() > 0) {	
				//draw the last point
				mPaint.setColor(colorsArray.get(line));					
				canvas.drawCircle(prevX, prevY, parameters.VERTEX_POINT_WIDTH.getValue(), mPaint);
			}
			
			if (parameters.SHOW_VERTEX_LABELS.getValue() && values.size() > 0) { 
				//draw the last valuelabel
				String label =  labelValueToString(values.get(values.size()-1), true);
				mPaint.setColor(parameters.VERTEX_LABELS_TEXT_COLOR.getValue());

				float txtSize = adjustTextSize(label, vertexLabelTextSize, spaceBetweenPoints);
				mPaint.setTextSize(txtSize);		
				
				float labelY;
						
				if (values.get(values.size()-1) >=0) {	
					labelY = prevY - parameters.VERTEX_TO_VERTEX_LABEL.getValue() 
							- parameters.VERTEX_POINT_WIDTH.getValue();	
					
					if (labelY - bounds.height() < plotTopY) {
						labelY = prevY + parameters.VERTEX_TO_VERTEX_LABEL.getValue()  
								+ bounds.height() + parameters.VERTEX_POINT_WIDTH.getValue();	
					}
				}
				else{
					labelY = prevY + parameters.VERTEX_TO_VERTEX_LABEL.getValue() 
							+  bounds.height() + parameters.VERTEX_POINT_WIDTH.getValue() ;	
	
					if (labelY > plotBottomY) {
						labelY = prevY - parameters.VERTEX_TO_VERTEX_LABEL.getValue()  
								- parameters.VERTEX_POINT_WIDTH.getValue();	
					}
				}					
				canvas.drawText(label, prevX , labelY, mPaint);				
			}
		}
		
	}
}
